<!DOCTYPE html>
<html lang="zh-Hans">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.3.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/blog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/blog/css/main.css">

<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext">
<link rel="stylesheet" href="/blog/lib/font-awesome/css/font-awesome.min.css">


<script id="hexo-configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    hostname: new URL('https://guodh.gitee.io/blog').hostname,
    root: '/blog/',
    scheme: 'Pisces',
    version: '7.7.1',
    exturl: false,
    sidebar: {"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},
    copycode: {"enable":true,"show_result":true,"style":null},
    back2top: {"enable":true,"sidebar":false,"scrollpercent":false},
    bookmark: {"enable":false,"color":"#222","save":"auto"},
    fancybox: false,
    mediumzoom: false,
    lazyload: false,
    pangu: false,
    comments: {"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},
    algolia: {
      appID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    },
    localsearch: {"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},
    path: 'search.xml',
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}
  };
</script>

  <meta name="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
<meta property="og:type" content="website">
<meta property="og:title" content="蛋蛋的小屋">
<meta property="og:url" content="https://guodh.gitee.io/blog/page/8/index.html">
<meta property="og:site_name" content="蛋蛋的小屋">
<meta property="og:description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
<meta property="og:locale">
<meta property="article:author" content="RunningEgg">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://guodh.gitee.io/blog/page/8/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome: true,
    isPost: false
  };
</script>

  <title>蛋蛋的小屋</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-meta">

    <div>
      <a href="/blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">蛋蛋的小屋</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
        <p class="site-subtitle">来了就看看吧，没有刀剑只有故事</p>
  </div>

  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>
</div>


<nav class="site-nav">
  
  <ul id="menu" class="menu">
        <li class="menu-item menu-item-home">

    <a href="/blog/" rel="section"><i class="fa fa-fw fa-home"></i>Home</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/blog/about/" rel="section"><i class="fa fa-fw fa-user"></i>About</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/blog/archives/" rel="section"><i class="fa fa-fw fa-archive"></i>Archives</a>

  </li>
        <li class="menu-item menu-item-book">

    <a href="/blog/book/" rel="section"><i class="fa fa-fw fa-archive"></i>book</a>

  </li>
  </ul>

</nav>
</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content">
            

  <div class="posts-expand">
        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/09/21/2017/cobar-drive%E7%90%86%E8%A7%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/09/21/2017/cobar-drive%E7%90%86%E8%A7%A3/" class="post-title-link" itemprop="url">Cobar-Drive理解</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-09-21 19:40:41" itemprop="dateCreated datePublished" datetime="2017-09-21T19:40:41+08:00">2017-09-21</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2017-09-30 21:27:49" itemprop="dateModified" datetime="2017-09-30T21:27:49+08:00">2017-09-30</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>架构组通知对cobar的driver进行升级，因为知道cobar已经很久没更新了，应该是公司内部做了升级，就去比对了一些代码。感觉看cobar的driver挺有收获，建议刚入门一两年的小伙伴都可以看看。看完这个Demo能够让一些新手理解如何使用TCP去调用Server，如何去解析从Server反馈的二进制文件，这里分享一下我的收获。</p>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/blog/2017/09/21/2017/cobar-drive%E7%90%86%E8%A7%A3/#more" rel="contents">
                Read more &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/09/17/2017/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E8%B0%83%E4%BC%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/09/17/2017/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E8%B0%83%E4%BC%98/" class="post-title-link" itemprop="url">线程池调优</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>
              

              <time title="Created: 2017-09-17 15:41:00 / Modified: 16:35:13" itemprop="dateCreated datePublished" datetime="2017-09-17T15:41:00+08:00">2017-09-17</time>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>这次的缘由也是群里的一个同学提到的：</p>
<blockquote>
<p>dubbo的线程池为什么缺省值是100？</p>
</blockquote>
<p>这里有两个问题：dubbo配置的这个线程池是管理的是短链接吗？那么在日常应用中线程池应该配置多大？<br><img src="https://olwr1lamu.qnssl.com/dubboThread.png" width="100%" height="100%" alt="dubbo默认配置"/></p>
<h2 id="NIO配置"><a href="#NIO配置" class="headerlink" title="NIO配置"></a>NIO配置</h2><p>可以看到thread和iothread是配置的不同的，那么这个让人想起一个比较简单的配置方法：</p>
<blockquote>
<ul>
<li>如果是CPU密集型应用，则线程池大小设置为N+1</li>
<li>如果是IO密集型应用，则线程池大小设置为2N+1</li>
</ul>
</blockquote>
<p>因为IO秘籍型CPU使用较少，是否可以这么理解，在使用NIO结构的时候，Acceptor可以值配置2N+1个或者较少的线程，而实际处理业务的场景的配置1个或者较多的线程，NIO示例如下图。<br><img src="https://olwr1lamu.qnssl.com/jiagou.png" width="100%" height="100%" alt="结构图"/></p>
<h2 id="dubbo线程池为什么可以配置100"><a href="#dubbo线程池为什么可以配置100" class="headerlink" title="dubbo线程池为什么可以配置100"></a>dubbo线程池为什么可以配置100</h2><p>学习dubbo官方文档可知：Dubbo协议缺省每服务每提供者每消费者使用单一长连接，如果数据量较大，可以使用多个连接。这个是典型的NIO模式。</p>
<blockquote>
<p>引用：作者微子Lee<br>链接：<a href="http://www.jianshu.com/p/32edaf9736a2">http://www.jianshu.com/p/32edaf9736a2</a></p>
</blockquote>
<blockquote>
<p>缺省协议，使用基于mina1.1.7+hessian3.2.1的tbremoting交互。<br>连接个数：单连接<br>连接方式：长连接<br>传输协议：TCP<br>传输方式：NIO异步传输<br>序列化：Hessian二进制序列化<br>适用范围：传入传出参数数据包较小（建议小于100K），消费者比提供者个数多，单一消费者无法压满提供者，尽量不要用dubbo协议传输大文件或超大字符串。<br>适用场景：常规远程服务方法调用</p>
</blockquote>
<h2 id="NIO的线程池大小是否会影响上下文切换"><a href="#NIO的线程池大小是否会影响上下文切换" class="headerlink" title="NIO的线程池大小是否会影响上下文切换"></a>NIO的线程池大小是否会影响上下文切换</h2><p>本质是IO的操作是不需要消耗CPU资源的。</p>
<blockquote>
<p>引用：作者王晓远<br>链接：<a href="https://www.zhihu.com/question/31259327/answer/147547667">https://www.zhihu.com/question/31259327/answer/147547667</a></p>
</blockquote>
<blockquote>
<p>DMA(Direct Memory Access), 直接存储器访问为什么不经过CPU？<br>I/O发展是经过一下一步步发展的</p>
<ol>
<li>CPU直接控制外围设备(硬盘磁带等), 在简单微处理器中常用</li>
<li>增加I/O模块, 将CPU与外围设备解耦, CPU只与I/O模块打交道. 只需定义好接口, CPU厂商和外围设备厂商就可以相互根据接口开发, 互不影响</li>
<li>增加中断方式, 还是经过I/O模块, 只不过I/O模块完成之后, 只需通知CPU即可, CPU在等待阶段完全可以去做其他事情, 提高CPU利用率.</li>
<li>I/O模块增加DMA控制器. 之前的阶段是每次只传输一个字, 就通知CPU, 就发起一次中断, CPU放到寄存器中, 再放到内存中. 这样CPU就会被连续的中断打断, 不断切换进程, 上下文, 效率很低.DMA控制器类似于一个小的CPU, 有自己的寄存器(记录主存地址和取到的字的count等). CPU可以发起一个DMA请求, 传入读写操作类型, 相关I/O设备地址, 内存的起始地址, 要操作的字数.然后DMA就可以获取总线的控制权, 将一大块内存和外部I/O读入或写出.等操作完成后, 再通知CPU. 释放总线控制权.</li>
</ol>
</blockquote>
<p>顺便说个，如果是Netty的handler中需要处理耗时任务，应该自己开启线程去处理，这个在业务中需要处理。</p>
<h2 id="CPU如何分配资源到多个线程池"><a href="#CPU如何分配资源到多个线程池" class="headerlink" title="CPU如何分配资源到多个线程池"></a>CPU如何分配资源到多个线程池</h2><p>笔者猜测应该是（例如Linux内核）决定如何去处理这些事情，每个单独的线程应该都是操作系统去管理调度那个CPU。</p>
<blockquote>
<p>引用：作者vczh<br>链接：<a href="https://www.zhihu.com/question/27406575/answer/149186136">https://www.zhihu.com/question/27406575/answer/149186136</a></p>
</blockquote>
<blockquote>
<p>CPU通常提供了一个framework，让操作系统利用中断来实现线程调度，在线程调度里实现锁。操作系统的线程并不是非要运行在某一个固定的逻辑核上面，而且线程通常绑定了一组寄存器的值（如果像VC++一样用__await实现coroutine的话，会有若干组）。所以基本的关系就是，CPU有物理核，上面再有逻辑核，操作系统通过把寄存器的值（和一些其他的东西）在不同的时候复制到不同的逻辑核上面来实现线程。</p>
</blockquote>
<p>还记得公司的架构师抽抽大神给我们的分享，如果上下文切换太过于频繁，可以通过将一个进程直接绑定在一个CPU上，这样反而能够提升效率。<br>可以通过Linux下的一个工具，taskset，将一个进程绑到CPU上。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">taskset [options] [mask | cpu-list] [pid|cmd [args...]]</span><br></pre></td></tr></table></figure>
<p>或者是利用国外的这个<a href="https://github.com/OpenHFT/Java-Thread-Affinity%E3%80%82">https://github.com/OpenHFT/Java-Thread-Affinity。</a><br>将进程绑定到CPU上，作为一个业务开发，可能实际应用场景不多，有兴趣的同学可以看看。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/08/13/2017/MQ%E7%9A%84Producer%E7%90%86%E8%A7%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/08/13/2017/MQ%E7%9A%84Producer%E7%90%86%E8%A7%A3/" class="post-title-link" itemprop="url">MQ的Producer理解</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>
              

              <time title="Created: 2017-08-13 14:33:12 / Modified: 16:27:13" itemprop="dateCreated datePublished" datetime="2017-08-13T14:33:12+08:00">2017-08-13</time>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>好久没写文章了，最近都是一直996，难得有机会空下来写点东西。事情的缘由是在一个开发群里面有人问了个问题:</p>
<blockquote>
<p>他是做网吧系统的，如果给每个网吧一个队列，上千个队列，RocketMQ是否能够支持？</p>
</blockquote>
<p>虽然平时自诩对MQ的理解还是可以的，但是这么一个问题好像我对MQ的理解仅仅是名词上的理解，所以赶紧打开ide重新看下MQ的源码。</p>
<h2 id="整体结构"><a href="#整体结构" class="headerlink" title="整体结构"></a>整体结构</h2><img src="https://olwr1lamu.qnssl.com/%E6%95%B4%E4%BD%93.png" alt="整体图"/>

<p>先从整体上理解MQ的组成，主要有Producer、Namesrv、Broker、Consumer组成。而Producer、Broker、Consumer之间的通信主要依靠Netty实现。整体的架构看似简单，但是真的去阅读源码，才发现MQ在可靠性以及扩展性上提供了很好的支持，这才是它作为一款生产级产品最大的特色。<br>可靠性体现在MQ的至少一次投递，以及能够追溯消息。而这次我们可以通过Producer来粗窥MQ的扩展性。</p>
<h2 id="Procuder如何发送消息"><a href="#Procuder如何发送消息" class="headerlink" title="Procuder如何发送消息"></a>Procuder如何发送消息</h2><img src="https://olwr1lamu.qnssl.com/producer.png" alt="Procuder发送过程"/>

<p>Procuder即消息的生产者，NameSrv中取获得当前Topic的相关信息。这里没有画出来的是，Procuder在内部缓存了一份TopicTable，只有当缓存失效时才去NameSrv获取。<br>获取到的TopicInfo中包含了一个List<Queue>, 这个东西很值得思考。在代码中可以看到，每一次消息投递，都对List<Queue>做了一个取余的处理。</p>
<p>那么就提出了一下几个问题：</p>
<hr>
<ol>
<li>这个Queue是什么东西？</li>
<li>这个Queue有什么用？</li>
<li>为何要维护多个Queue呢？搞一个不就行了吗？</li>
</ol>
<hr>
<p>打开类一看，长这个鸟样。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MessageQueue</span> <span class="keyword">implements</span> <span class="title">Comparable</span>&lt;<span class="title">MessageQueue</span>&gt;, <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String topic;</span><br><span class="line">    <span class="keyword">private</span> String brokerName;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> queueId;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>查阅了相关资料，来自<a href="https://jaskey.github.io/blog/2016/12/19/rocketmq-rebalance%E3%80%82">https://jaskey.github.io/blog/2016/12/19/rocketmq-rebalance。</a><br>可以看到通过让不同的Q分布在broker上实现均衡负载，可以让某些broker来负担多一点的Q，也可以平衡的分布Q。不必让消息绑定在Broker上，而是让消息于Q相绑定，用更小的颗粒度去控制消息。同时Q不保存消息的具体内容，而是能通过Q去追溯在Broker的位置。这一招厉害呀，扩展性瞬间提高了一大截。</p>
<h2 id="那个的问题"><a href="#那个的问题" class="headerlink" title="那个的问题"></a>那个的问题</h2><p>说回那个同学的问题，他应该是直接使用了上千的Topic，但是对于MQ来说，不同于Kafka，MQ能够支持上万的Topic(详情参见官方MQ于kafka的比较）。那么MQ是如何管理所拥有的Topic呢？下一次来分析下Broker的源码。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/05/19/2017/weex%E5%B0%8F%E5%AE%9E%E8%B7%B5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/05/19/2017/weex%E5%B0%8F%E5%AE%9E%E8%B7%B5/" class="post-title-link" itemprop="url">Weex小实践</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-05-19 19:53:00" itemprop="dateCreated datePublished" datetime="2017-05-19T19:53:00+08:00">2017-05-19</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2017-12-17 12:40:44" itemprop="dateModified" datetime="2017-12-17T12:40:44+08:00">2017-12-17</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>因为毕业要做一个Demo，而笔者的前端确实挫的可以，所以要找个合适的前端框架，要求如下：</p>
<ul>
<li>入门较快</li>
<li>有比较大的前景</li>
</ul>
<p>认（sui）真（bian）的思考之后，决定用Weex来实现前端，事后看来Weex确实达到我的要求。</p>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/blog/2017/05/19/2017/weex%E5%B0%8F%E5%AE%9E%E8%B7%B5/#more" rel="contents">
                Read more &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/04/23/2018/NIOServer/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/04/23/2018/NIOServer/" class="post-title-link" itemprop="url">如何自己实现一个NIO的Server</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-04-23 16:29:58" itemprop="dateCreated datePublished" datetime="2017-04-23T16:29:58+08:00">2017-04-23</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2018-06-18 13:05:15" itemprop="dateModified" datetime="2018-06-18T13:05:15+08:00">2018-06-18</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>话说笔者最近在看一些网络通信方面的东西，恰巧看到一位大牛做了一个NIO的server的Demo。Demo虽小，但是五脏俱全，能够很好的一窥NIO服务的架构。<br>源码地址：<a href="https://github.com/jjenkov/java-nio-server">https://github.com/jjenkov/java-nio-server</a></p>
<h1 id="整体结构"><a href="#整体结构" class="headerlink" title="整体结构"></a>整体结构</h1><img src="https://olwr1lamu.qnssl.com/NIOpackge.png" width="50%" height="50%" alt="结构图"/>
整体结构如图所示，刚开始可能不知道这个Server是一个怎样的结构，但是作者很善良的给了个example，那么就先从main方法进入。

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Server server = <span class="keyword">new</span> Server(<span class="number">9999</span>, <span class="keyword">new</span> HttpMessageReaderFactory(), messageProcessor);</span><br><span class="line">server.start();</span><br></pre></td></tr></table></figure>
<p>main方法中，主要初始化了项目中的Server，以及启动Server。好，那么就可以直接来看整个项目真正的启动类。</p>
<h2 id="Server"><a href="#Server" class="headerlink" title="Server"></a>Server</h2><p>直接来看Server中的start吧，看看一个Server需要启动哪些脚手架：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">	      <span class="comment">// 循环创建socket放入Queue</span></span><br><span class="line">        <span class="keyword">this</span>.socketAccepter  = <span class="keyword">new</span> SocketAccepter(tcpPort, socketQueue);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// NIO循环遍历，查询可以进行操作的channel</span></span><br><span class="line">        <span class="comment">// 这里的入参都是来自Server的初始化的时候的构造函数，而且还包含一个Factory方法，这种设计类的思路可以借鉴一下</span></span><br><span class="line">        <span class="keyword">this</span>.socketProcessor = <span class="keyword">new</span> SocketProcessor(socketQueue, readBuffer, writeBuffer,  <span class="keyword">this</span>.messageReaderFactory, <span class="keyword">this</span>.messageProcessor);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 启动线程</span></span><br><span class="line">		    accepterThread.start();</span><br><span class="line">        processorThread.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过上面的类，我们可以看到，Server中主要启动了，两个线程，分别是accepterThread和processorThread。</p>
<h2 id="架构组成"><a href="#架构组成" class="headerlink" title="架构组成"></a>架构组成</h2><p>为了方便理解，我直接把整个NIO的架构给贴出来吧。<br><img src="https://olwr1lamu.qnssl.com/jiagou.png" alt="NIO架构图"/><br>主要包括Acceptor和SocketProcessor，两者之间通过Queue连接。</p>
<h2 id="Acceptor"><a href="#Acceptor" class="headerlink" title="Acceptor"></a>Acceptor</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 打开Socket</span></span><br><span class="line"><span class="keyword">try</span>&#123;</span><br><span class="line">     <span class="keyword">this</span>.serverSocket = ServerSocketChannel.open();</span><br><span class="line">     <span class="keyword">this</span>.serverSocket.bind(<span class="keyword">new</span> InetSocketAddress(tcpPort));</span><br><span class="line">&#125; <span class="keyword">catch</span>(IOException e)&#123;</span><br><span class="line">     <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">while</span>(<span class="keyword">true</span>)&#123;</span><br><span class="line">      <span class="keyword">try</span>&#123;</span><br><span class="line">	       <span class="comment">// socketChannel.accept接收connection</span></span><br><span class="line">           SocketChannel socketChannel = <span class="keyword">this</span>.serverSocket.accept();</span><br><span class="line">           <span class="comment">// 将Socket放入队列</span></span><br><span class="line">           <span class="keyword">this</span>.socketQueue.add(<span class="keyword">new</span> Socket(socketChannel));</span><br><span class="line"></span><br><span class="line">          &#125; <span class="keyword">catch</span>(IOException e)&#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">          &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>socketChannel可以设置为非阻塞模式，serverSocketChannel.configureBlocking(false);<br>这样socketChannel接收到Connoection就会立刻返回，没有则返回null。直接将socketChannel放入Channel，由SocketProcessor去负责检查Channel是否就绪。同时，需要Processor处理，返回为null的情况。</p>
<h2 id="SocketProcessor"><a href="#SocketProcessor" class="headerlink" title="SocketProcessor"></a>SocketProcessor</h2><p>SocketProcessor中维护了一个readSelector，其中注册了来自Queue中的Socket。<br>readSelector是由Java NIO包中package java.nio.channels.Selector提供。<br>注册分几步走：</p>
<ol>
<li>遍历Queue中的Socket</li>
<li>初始化Socket中的IMessageReader，socketChannel中的configureBlocking设置为false（有个问题：为什么是在Procesor设置，而不是Acceptor中设置？）</li>
<li>将Socket注册到Selector中</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SelectionKey key = newSocket.socketChannel.register(<span class="keyword">this</span>.readSelector, SelectionKey.OP_READ);</span><br><span class="line">key.attach(newSocket);</span><br></pre></td></tr></table></figure>
<p>通过上面两个函数，将Socket中的socketChannel注册到了Selector中，同时返回SelectionKey。<br>4. 检查readSelector<br>5. 就绪的SocketChannel，则使用Socket初始化的messageReader，读取Message<br>6. 将SelectionKey从readSelector取消，并关闭SocketChannel</p>
<h2 id="Socket类"><a href="#Socket类" class="headerlink" title="Socket类"></a>Socket类</h2><p>这里可以来看下作者设计的Socket这个类：<br><img src="https://olwr1lamu.qnssl.com/Socket2.png" width="100%" height="75%" alt="Socket结构"/><br>这里一个很好的设计是，messageRead是有Factory产生，而Factory是从messageProcessor从构造类中导入，提供了很好的扩展性。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IMessageReader</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(MessageBuffer readMessageBuffer)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">read</span><span class="params">(Socket socket, ByteBuffer byteBuffer)</span> <span class="keyword">throws</span> IOException</span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Message&gt; <span class="title">getMessages</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="字节流"><a href="#字节流" class="headerlink" title="字节流"></a>字节流</h2><p>同时，在HttpUtil中还有个处理请求字节流的工具类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> endIndex = HttpUtil.parseHttpRequest(<span class="keyword">this</span>.nextMessage.sharedArray, <span class="keyword">this</span>.nextMessage.offset, <span class="keyword">this</span>.nextMessage.offset + <span class="keyword">this</span>.nextMessage.length, (HttpHeaders) <span class="keyword">this</span>.nextMessage.metaData);</span><br><span class="line"><span class="keyword">if</span>(endIndex != -<span class="number">1</span>)&#123;</span><br><span class="line">	Message message = <span class="keyword">this</span>.messageBuffer.getMessage();</span><br><span class="line">	message.metaData = <span class="keyword">new</span> HttpHeaders();</span><br><span class="line">	message.writePartialMessageToMessage(nextMessage, endIndex);</span><br><span class="line">	completeMessages.add(nextMessage);</span><br><span class="line">	nextMessage = message;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>不过，知道怎么用就差不多了（其实是懒）。</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>通过整个分析，可以了解SocketChannel，Selector的用法，以及对NIO Server的架构有了一个理性的认识。对于以后自己去处理NIO的一些东西，是非常有价值的，非常鼓励大家，能够去看看这个小Demo的源码。</p>
<p>同时，推荐两个开源项目，有利用到NIO，而且都很有意思：<br>Rider: <a href="https://my.oschina.net/alchemystar/blog/865237">https://my.oschina.net/alchemystar/blog/865237</a><br>Mycat</p>
<p>另外，介绍Java的一个Buffer，DirectBuffer。不同于一般的Buffer，DirectBuffer属于内存Buffer，虽然DirectBuffer的创建相较于普通Buffer耗费时间，但是对于一些大的IO，如果需要从内存 -&gt; jvm -&gt; 内存，DirectBuffer能够提供更高的效率。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Buffer</span> </span>&#123;</span><br><span class="line">	<span class="comment">// 0 &lt;= mark &lt;= position &lt;= limit &lt;= capacity	</span></span><br><span class="line">	<span class="keyword">int</span> capacity,limit,position,mark;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> Buffer <span class="title">flip</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 将limit设置为当前position，同时position设置为0，准备开始读数据</span></span><br><span class="line">	&#125;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> Buffer <span class="title">rewind</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">// 将position设置为0，mark设置为-1，准备开始写数据</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/03/20/2017/lock%E5%92%8Clockinterruptibly/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/03/20/2017/lock%E5%92%8Clockinterruptibly/" class="post-title-link" itemprop="url">几种获得锁的方法的区别</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-03-20 23:31:41" itemprop="dateCreated datePublished" datetime="2017-03-20T23:31:41+08:00">2017-03-20</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2017-10-02 11:19:50" itemprop="dateModified" datetime="2017-10-02T11:19:50+08:00">2017-10-02</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>在Java中有好几种拿到锁的方法，那么他们之间的区别是什么呢？今天就来讲讲Lock(), Trylock(), Lockinterruptibly()这个几个方法的区别。虽然比较简单，但是一步步到位，从简单的开始。</p>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/blog/2017/03/20/2017/lock%E5%92%8Clockinterruptibly/#more" rel="contents">
                Read more &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/03/16/2017/canal%E6%9E%B6%E6%9E%84/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/03/16/2017/canal%E6%9E%B6%E6%9E%84/" class="post-title-link" itemprop="url">Canal架构理解</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-03-16 22:06:43" itemprop="dateCreated datePublished" datetime="2017-03-16T22:06:43+08:00">2017-03-16</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2017-09-30 21:28:36" itemprop="dateModified" datetime="2017-09-30T21:28:36+08:00">2017-09-30</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>目标：理解Canal系统中的架构设计</p>
</blockquote>
<p>数据库的数据同步的解决方案，一般是基于trigger（触发器）或者是基于log（日志）。基于trigger的模式侵入性比较大，而且对性能也有影响，所以一般采用后者。读取后的log可以通过RocketMQ或者Kafka将日志发送到需要的平台进行处理。<br>本文主要介绍Canal的架构组成，对Canal有个理性的了解。</p>
<h2 id="Canal的特点"><a href="#Canal的特点" class="headerlink" title="Canal的特点"></a>Canal的特点</h2><blockquote>
<p>基于自己整理，只能说个大概</p>
</blockquote>
<ol>
<li>支持HA（高可用） 采用Zookeeper保存各个子模块的状态，让整个增量订阅和消费模块实现无状态化。</li>
<li>并发模式的设计 采用disruptor的设计</li>
<li>支持增量订阅/消费模式 </li>
</ol>
<h2 id="instance架构"><a href="#instance架构" class="headerlink" title="instance架构"></a>instance架构</h2><img src="https://olwr1lamu.qnssl.com/canal%E6%9E%B6%E6%9E%84.jpg" width="75%" alt="canal总体架构"/>

<p>一个instance对应一个数据，server中可以有多个instance</p>
<ul>
<li>eventParser 数据连接，模拟slave和master通信</li>
<li>eventSink 对数据进行处理</li>
<li>eventStore 存储数据</li>
<li>metaManager 消费订阅管理</li>
</ul>
<p>官方（阿里）部署方式：部署8个队列，4M + 100ms的获取</p>
<h3 id="1-EventParser"><a href="#1-EventParser" class="headerlink" title="1.EventParser"></a>1.EventParser</h3><p>如何支持多线程模型？</p>
<h3 id="2-EventSink"><a href="#2-EventSink" class="headerlink" title="2.EventSink"></a>2.EventSink</h3><p>sink 中文：水槽</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">EntryEventSink</span> </span>&#123;</span><br><span class="line">	<span class="keyword">public</span> CanalEventStore&lt;Event&gt; eventStore;</span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="comment">//初始化Handler</span></span><br><span class="line">        <span class="keyword">for</span> (CanalEventDownStreamHandler handler : getHandlers()) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!handler.isStart()) &#123;</span><br><span class="line">                handler.start();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">sinkDate</span><span class="params">(List&lt;CanalEntry.Entry&gt; entrys, InetSocketAddress remoteAddress)</span> </span>&#123;</span><br><span class="line">	    <span class="comment">// 过滤</span></span><br><span class="line">	    doFilter(events);</span><br><span class="line">	    <span class="comment">// 对evetn实际操作</span></span><br><span class="line">	    doSink(events);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="3-EventStore"><a href="#3-EventStore" class="headerlink" title="3.EventStore"></a>3.EventStore</h3><p>首先Canal采用了Distruptor的缓冲区，与BlockedQueue相对应。<br>如何设计的是消费订阅模型，放到内存中有什么意义？？</p>
<h3 id="4-消费订阅模型（pull-push模型）"><a href="#4-消费订阅模型（pull-push模型）" class="headerlink" title="4.消费订阅模型（pull/push模型）"></a>4.消费订阅模型（pull/push模型）</h3><p>有序/无序的问题</p>
<h2 id="Server模型"><a href="#Server模型" class="headerlink" title="Server模型"></a>Server模型</h2><ul>
<li>Embeded（嵌入式）</li>
<li>Netty（网络）pull模型</li>
</ul>
<h3 id="增量订阅-消费设计"><a href="#增量订阅-消费设计" class="headerlink" title="增量订阅/消费设计"></a>增量订阅/消费设计</h3><p>Message getWithoutAck(int batchSize);<br>void rollback(long batchId);        // 回滚并重新获得数据，需要传参数batchid<br>void ack(long batchId);        // 确认已经消费，需要传参数batchid<br>支持api流式。demo:延迟很高，不断get，等后期在ack();<br><img src="https://olwr1lamu.qnssl.com/%E6%B5%81%E5%BC%8Fapi.jpg"  alt="流式api"/><br>每次get都会产生一个mark，之后的get都是总最后的一个mark开始取。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/03/08/2017/canal%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9E%E6%8E%A5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/03/08/2017/canal%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9E%E6%8E%A5/" class="post-title-link" itemprop="url">Canal客户端连接</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-03-08 20:43:10" itemprop="dateCreated datePublished" datetime="2017-03-08T20:43:10+08:00">2017-03-08</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2017-09-30 21:28:25" itemprop="dateModified" datetime="2017-09-30T21:28:25+08:00">2017-09-30</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>目标：顺利把canal运行起来<br>俗话说：磨刀不误砍柴功，所以先从搭建canal开始</p>
</blockquote>
<p>配置：centos7，mysql5.6</p>
<h3 id="1-mysql开启binlog"><a href="#1-mysql开启binlog" class="headerlink" title="1. mysql开启binlog"></a>1. mysql开启binlog</h3>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/blog/2017/03/08/2017/canal%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9E%E6%8E%A5/#more" rel="contents">
                Read more &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/02/25/2017/Executor%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/02/25/2017/Executor%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/" class="post-title-link" itemprop="url">Executor原理分析</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>
              

              <time title="Created: 2017-02-25 00:14:51 / Modified: 00:18:03" itemprop="dateCreated datePublished" datetime="2017-02-25T00:14:51+08:00">2017-02-25</time>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>（未写完……）<br>看了一篇文章，讲的是一分钟理解连接池的实现，其中讲到：连接池的关键是维护了两个数组，一个数组记录游标，另一个数组保证加锁。<br>链接：<a href="https://0x9.me/Zz9hp%EF%BC%88%E8%BD%AC%E6%88%90%E4%BA%86%E7%9F%AD%E7%A0%81%EF%BC%8C%E6%9C%89%E5%8F%AF%E8%83%BD%E5%A4%B1%E6%95%88%EF%BC%89">https://0x9.me/Zz9hp（转成了短码，有可能失效）</a><br>讲的很有意思，但是做为一个JAVAER，我对JAVA的底层如何实现“池”的概念更感兴趣，那么这一次我们就从JAVA的源码角度来看看，JAVA到底是如何实现创建线程池，拿到线程，放回线程池。<br>先从执行类开始说起，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Executor</span> </span>&#123;</span><br><span class="line">	<span class="comment">//所有的Executor所继承的接口，可以发现execute是没有返回值的，所有的返回值都是保存在command（也就是Future）里面的。Executor其实就是“喂喂喂，所有的task都走这里”。</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ExecutorService</span> <span class="keyword">extends</span> <span class="title">Executor</span></span>&#123;</span><br><span class="line">	<span class="comment">// 已经抽象出了一个Executor（以及下一步的线程池）的样子</span></span><br><span class="line">	<span class="function"><span class="keyword">boolean</span> <span class="title">awaitTermination</span><span class="params">(<span class="keyword">long</span> time, TimeUnit unit)</span></span>;</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span></span>;</span><br><span class="line">	invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在这里需要说明一下的是，在ExecutorService的实现类中，有很多有价值的CLASS：<br>ForkJoinPool、ScheduleThreadPoolExecutor、ThreadPoolExecutor。<br>还记得在看《七周七并发中》的一个很有价值的问题：<br>** ForkJoinPool和ThreadPoolExecutor有什么区别？**<br>欢迎大家自己想想。</p>
<p>回归正题，继续看线程池。先看下ThreadPoolExecutor的构造类。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ThreadPoolExecutor</span><span class="params">(<span class="keyword">int</span> corePoolSize,	// 最小保持的线程数</span></span></span><br><span class="line"><span class="function"><span class="params">                          <span class="keyword">int</span> maximumPoolSize, // 最大保持的线程数</span></span></span><br><span class="line"><span class="function"><span class="params">                          <span class="keyword">long</span> keepAliveTime,</span></span></span><br><span class="line"><span class="function"><span class="params">                          TimeUnit unit,</span></span></span><br><span class="line"><span class="function"><span class="params">                          BlockingQueue&lt;Runnable&gt; workQueue,</span></span></span><br><span class="line"><span class="function"><span class="params">                          ThreadFactory threadFactory, // 生成Thread的工厂类</span></span></span><br><span class="line"><span class="function"><span class="params">                          RejectedExecutionHandler handler // 用来处理线程池满了或者队列满了的处理类)</span>；                     </span></span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultThreadFactory</span> <span class="keyword">implements</span> <span class="title">ThreadFactory</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> AtomicInteger poolNumber = <span class="keyword">new</span> AtomicInteger(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ThreadGroup group;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> AtomicInteger threadNumber = <span class="keyword">new</span> AtomicInteger(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String namePrefix;</span><br><span class="line"></span><br><span class="line">    DefaultThreadFactory() &#123;</span><br><span class="line">        SecurityManager s = System.getSecurityManager(); <span class="comment">//是保证上下文切换吗？还是为了安全什么的？</span></span><br><span class="line">        group = (s != <span class="keyword">null</span>) ? s.getThreadGroup() :</span><br><span class="line">                              Thread.currentThread().getThreadGroup();</span><br><span class="line">        namePrefix = <span class="string">&quot;pool-&quot;</span> +</span><br><span class="line">                      poolNumber.getAndIncrement() +</span><br><span class="line">                     <span class="string">&quot;-thread-&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Thread <span class="title">newThread</span><span class="params">(Runnable r)</span> </span>&#123;</span><br><span class="line">        Thread t = <span class="keyword">new</span> Thread(group, r,</span><br><span class="line">                              namePrefix + threadNumber.getAndIncrement(),</span><br><span class="line">                              <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">if</span> (t.isDaemon())</span><br><span class="line">            t.setDaemon(<span class="keyword">false</span>);</span><br><span class="line">        <span class="keyword">if</span> (t.getPriority() != Thread.NORM_PRIORITY)</span><br><span class="line">            t.setPriority(Thread.NORM_PRIORITY);</span><br><span class="line">        <span class="keyword">return</span> t;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>继续回到</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadPoolExecutor</span> </span>&#123;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (command == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">        <span class="comment">/**这里有三个步骤：</span></span><br><span class="line"><span class="comment">         * 1.如果corePool数量未满，则调用addworker原子性添加线程</span></span><br><span class="line"><span class="comment">         * 2.将command放入到队列，并且二次检查，检查线程的数量以及线程池是否挂掉</span></span><br><span class="line"><span class="comment">         * 3.如果线程数未满，则添加线程，如果线程池挂掉则退出</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">int</span> c = ctl.get(); </span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">        * private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));</span></span><br><span class="line"><span class="comment">        * ctl 通过位运算保存了线程数以及线程状态，有意思</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        <span class="keyword">if</span> (workerCountOf(c) &lt; corePoolSize) &#123;</span><br><span class="line">            <span class="keyword">if</span> (addWorker(command, <span class="keyword">true</span>))</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            c = ctl.get();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (isRunning(c) &amp;&amp; workQueue.offer(command)) &#123;</span><br><span class="line">            <span class="keyword">int</span> recheck = ctl.get();</span><br><span class="line">            <span class="keyword">if</span> (! isRunning(recheck) &amp;&amp; remove(command))</span><br><span class="line">                reject(command);</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (workerCountOf(recheck) == <span class="number">0</span>)</span><br><span class="line">                addWorker(<span class="keyword">null</span>, <span class="keyword">false</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (!addWorker(command, <span class="keyword">false</span>))</span><br><span class="line">            reject(command);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> ReentrantLock mainLock = <span class="keyword">this</span>.mainLock;</span><br><span class="line">        mainLock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            checkShutdownAccess();</span><br><span class="line">            advanceRunState(SHUTDOWN);</span><br><span class="line">            interruptIdleWorkers();</span><br><span class="line">            onShutdown(); <span class="comment">// hook for ScheduledThreadPoolExecutor</span></span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            mainLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        tryTerminate();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>好了，下面就是重头戏了，为了明显点特意从ThreadPoolExecutor中的execute()了出来，已经快睡着的同学可以好好看了：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">addWorker</span><span class="params">(Runnable firstTask, <span class="keyword">boolean</span> core)</span> </span>&#123;</span><br><span class="line">        retry:</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">int</span> c = ctl.get();</span><br><span class="line">            <span class="keyword">int</span> rs = runStateOf(c);</span><br><span class="line">            <span class="comment">// 关闭时：检测队列是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (rs &gt;= SHUTDOWN &amp;&amp;</span><br><span class="line">                ! (rs == SHUTDOWN &amp;&amp;</span><br><span class="line">                   firstTask == <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">                   ! workQueue.isEmpty()))</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">int</span> wc = workerCountOf(c);</span><br><span class="line">                <span class="keyword">if</span> (wc &gt;= CAPACITY ||</span><br><span class="line">                    wc &gt;= (core ? corePoolSize : maximumPoolSize))</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                <span class="keyword">if</span> (compareAndIncrementWorkerCount(c))</span><br><span class="line">                    <span class="keyword">break</span> retry;</span><br><span class="line">                c = ctl.get();  <span class="comment">// Re-read ctl</span></span><br><span class="line">                <span class="keyword">if</span> (runStateOf(c) != rs)</span><br><span class="line">                    <span class="keyword">continue</span> retry;</span><br><span class="line">                <span class="comment">// else CAS failed due to workerCount change; retry inner loop</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">boolean</span> workerStarted = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">boolean</span> workerAdded = <span class="keyword">false</span>;</span><br><span class="line">        Worker w = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            w = <span class="keyword">new</span> Worker(firstTask);</span><br><span class="line">            <span class="keyword">final</span> Thread t = w.thread;</span><br><span class="line">            <span class="keyword">if</span> (t != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">final</span> ReentrantLock mainLock = <span class="keyword">this</span>.mainLock;</span><br><span class="line">                mainLock.lock();</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// Recheck while holding lock.</span></span><br><span class="line">                    <span class="comment">// Back out on ThreadFactory failure or if</span></span><br><span class="line">                    <span class="comment">// shut down before lock acquired.</span></span><br><span class="line">                    <span class="keyword">int</span> rs = runStateOf(ctl.get());</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (rs &lt; SHUTDOWN ||</span><br><span class="line">                        (rs == SHUTDOWN &amp;&amp; firstTask == <span class="keyword">null</span>)) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (t.isAlive()) <span class="comment">// precheck that t is startable</span></span><br><span class="line">                            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalThreadStateException();</span><br><span class="line">                        workers.add(w);</span><br><span class="line">                        <span class="keyword">int</span> s = workers.size();</span><br><span class="line">                        <span class="keyword">if</span> (s &gt; largestPoolSize)</span><br><span class="line">                            largestPoolSize = s;</span><br><span class="line">                        workerAdded = <span class="keyword">true</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    mainLock.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (workerAdded) &#123;</span><br><span class="line">                    t.start();</span><br><span class="line">                    workerStarted = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (! workerStarted)</span><br><span class="line">                addWorkerFailed(w);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> workerStarted;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>下面是实际工作的类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Worker</span></span></span><br><span class="line"><span class="class">        <span class="keyword">extends</span> <span class="title">AbstractQueuedSynchronizer</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * This class will never be serialized, but we provide a</span></span><br><span class="line"><span class="comment">         * serialVersionUID to suppress a javac warning.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">6138294804551838833L</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/** Thread this worker is running in.  Null if factory fails. */</span></span><br><span class="line">        <span class="keyword">final</span> Thread thread;</span><br><span class="line">        <span class="comment">/** Initial task to run.  Possibly null. */</span></span><br><span class="line">        Runnable firstTask;</span><br><span class="line">        <span class="comment">/** Per-thread task counter */</span></span><br><span class="line">        <span class="keyword">volatile</span> <span class="keyword">long</span> completedTasks;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Creates with given first task and thread from ThreadFactory.</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> firstTask the first task (null if none)</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        Worker(Runnable firstTask) &#123;</span><br><span class="line">            setState(-<span class="number">1</span>); <span class="comment">// inhibit interrupts until runWorker</span></span><br><span class="line">            <span class="keyword">this</span>.firstTask = firstTask;</span><br><span class="line">            <span class="keyword">this</span>.thread = getThreadFactory().newThread(<span class="keyword">this</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/** Delegates main run loop to outer runWorker  */</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            runWorker(<span class="keyword">this</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Lock methods</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// The value 0 represents the unlocked state.</span></span><br><span class="line">        <span class="comment">// The value 1 represents the locked state.</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">isHeldExclusively</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> getState() != <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> unused)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, <span class="number">1</span>)) &#123;</span><br><span class="line">                setExclusiveOwnerThread(Thread.currentThread());</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryRelease</span><span class="params">(<span class="keyword">int</span> unused)</span> </span>&#123;</span><br><span class="line">            setExclusiveOwnerThread(<span class="keyword">null</span>);</span><br><span class="line">            setState(<span class="number">0</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span>        </span>&#123; acquire(<span class="number">1</span>); &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">tryLock</span><span class="params">()</span>  </span>&#123; <span class="keyword">return</span> tryAcquire(<span class="number">1</span>); &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span>      </span>&#123; release(<span class="number">1</span>); &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isLocked</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> isHeldExclusively(); &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">interruptIfStarted</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            Thread t;</span><br><span class="line">            <span class="keyword">if</span> (getState() &gt;= <span class="number">0</span> &amp;&amp; (t = thread) != <span class="keyword">null</span> &amp;&amp; !t.isInterrupted()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    t.interrupt();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (SecurityException ignore) &#123;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

        
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
    <link itemprop="mainEntityOfPage" href="https://guodh.gitee.io/blog/2017/02/22/2017/jedis%E5%AE%9E%E8%B7%B5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/avatar.gif">
      <meta itemprop="name" content="RunningEgg">
      <meta itemprop="description" content="写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="蛋蛋的小屋">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          
            <a href="/blog/2017/02/22/2017/jedis%E5%AE%9E%E8%B7%B5/" class="post-title-link" itemprop="url">Jedis实践</a>
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-02-22 18:36:55" itemprop="dateCreated datePublished" datetime="2017-02-22T18:36:55+08:00">2017-02-22</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2017-10-02 11:13:21" itemprop="dateModified" datetime="2017-10-02T11:13:21+08:00">2017-10-02</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>最近在写业务代码的时候用到的SETNX，但是在牛肉的提醒下才发现我对SETNX的理解有很多的问题，果然还是NO BI BI, SHOW ME THE CODE比较实际。</p>
<h3 id="SETNX"><a href="#SETNX" class="headerlink" title="SETNX"></a>SETNX</h3><p>先讲下原理：<br>SETNX提供了一个锁的概念，通过判断返回值（0或者1）来执行不同的逻辑，包含了GET和SET的逻辑，在原来GET或者SET的场景直接使用SETNX就好。通过使用SETNX能够应用于业务去重，以及防止缓存穿透等场景。<br>一般做法：在更新缓存前，先判断SETNX是否存在，保证只有一个线程进入update（获得资源），其余的线程则是等待或者读取以前缓存的数据。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// redis提供的setnx没有过期时间，从2.6.12之后set涵盖setnx功能，提供过期</span></span><br><span class="line"><span class="comment">// String statusCode = redis.set(&quot;lock&quot;, value, &quot;NX&quot;, &quot;EX&quot;, expire); 和下面使用setnx相同</span></span><br><span class="line">String statusCode = redis.setnx(<span class="string">&quot;lock&quot;</span>, value);</span><br><span class="line"><span class="keyword">if</span>( <span class="string">&quot;OK&quot;</span> == statusCode) &#123;</span><br><span class="line">  <span class="comment">// 成功获得锁，更新数据场景</span></span><br><span class="line">  db.select(....);</span><br><span class="line">  <span class="comment">// 这里可能比较耗时，例如200ms</span></span><br><span class="line">  redis.update();</span><br><span class="line">  redis.del(key);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  <span class="comment">// 没有获得锁，使用过期数据场景</span></span><br><span class="line">  redis.get(); <span class="comment">// or 或者一直等待</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里需要像类似我这种小白的人，说明的是Redis是单线程的，提供一个队列模型，能够保证有序的执行每个command。同时，通过各种资料查的Redis的SET和GET都是原子性的（实验方法：SET1到1000，同时GET，发现数据并没有影响）。<br>但是在SETNX和Del之间，会执行业务逻辑，业务逻辑并不是原子性的，在这段时间内Redis还会有多个GET。这部分数据会在大并发的时候，造成缓存穿透，有可能导致DB挂掉。<br>start-&gt; SETNX -&gt; GET -&gt; GET -&gt; …. -&gt;Get-&gt;Del -&gt; end<br>Get 的长度取决于业务逻辑执行时间。</p>
<p>SETNX只是提供了一个锁的概念，并不是存储数据（我一直以为SETNX是用来存数据的……尴尬）<br>参考: <a href="http://huoding.com/2015/09/14/463">http://huoding.com/2015/09/14/463</a></p>
<h3 id="Jedis源码"><a href="#Jedis源码" class="headerlink" title="Jedis源码"></a>Jedis源码</h3><p>在排查的过程中，顺便看了下Jedis的源码，这里也一并记录下来希望对人有帮助</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> Connection <span class="title">sendCommand</span><span class="params">(<span class="keyword">final</span> Command cmd, <span class="keyword">final</span> <span class="keyword">byte</span>[]... args)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      connect();</span><br><span class="line">      Protocol.sendCommand(outputStream, cmd, args);</span><br><span class="line">      pipelinedCommands++;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (JedisConnectionException ex) &#123;</span><br><span class="line">      <span class="comment">/**发送有问题数据，会返回errorMessage*/</span></span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">        String errorMessage = Protocol.readErrorLineIfPossible(inputStream);</span><br><span class="line">        <span class="keyword">if</span> (errorMessage != <span class="keyword">null</span> &amp;&amp; errorMessage.length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">          ex = <span class="keyword">new</span> JedisConnectionException(errorMessage, ex.getCause());</span><br><span class="line">        &#125;</span><br><span class="line">      &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">      broken = <span class="keyword">true</span>;</span><br><span class="line">      <span class="keyword">throw</span> ex;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<p>可以看到jedis先把args塞到command中，然后把command放到Protocal的队列中</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sendCommand</span><span class="params">(<span class="keyword">final</span> RedisOutputStream os, <span class="keyword">final</span> <span class="keyword">byte</span>[] command,</span></span></span><br><span class="line"><span class="function"><span class="params">      <span class="keyword">final</span> <span class="keyword">byte</span>[]... args)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      os.write(ASTERISK_BYTE);</span><br><span class="line">      os.writeIntCrLf(args.length + <span class="number">1</span>);</span><br><span class="line">      os.write(DOLLAR_BYTE);</span><br><span class="line">      os.writeIntCrLf(command.length);</span><br><span class="line">      os.write(command);</span><br><span class="line">      os.writeCrLf(); <span class="comment">// --&gt;作用是flushBuffer,立刻将缓存的数据写入预期目标</span></span><br><span class="line"></span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">final</span> <span class="keyword">byte</span>[] arg : args) &#123;</span><br><span class="line">        os.write(DOLLAR_BYTE);</span><br><span class="line">        os.writeIntCrLf(arg.length);</span><br><span class="line">        os.write(arg);</span><br><span class="line">        os.writeCrLf(); <span class="comment">// --&gt;作用是flushBuffer</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> JedisConnectionException(e);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

  </div>

  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/blog/page/7/"><i class="fa fa-angle-left" aria-label="Previous page"></i></a><a class="page-number" href="/blog/">1</a><span class="space">&hellip;</span><a class="page-number" href="/blog/page/7/">7</a><span class="page-number current">8</span><a class="page-number" href="/blog/page/9/">9</a><a class="extend next" rel="next" href="/blog/page/9/"><i class="fa fa-angle-right" aria-label="Next page"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let activeClass = CONFIG.comments.activeClass;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">RunningEgg</p>
  <div class="site-description" itemprop="description">写下我的点点滴滴，写下我对这个世界的看法，还有我内心锁崇拜的技术</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/blog/archives/">
        
          <span class="site-state-item-count">89</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">24</span>
        <span class="site-state-item-name">tags</span>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="mailto:guodh_1@163.com" title="E-Mail → mailto:guodh_1@163.com" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">RunningEgg</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> v5.3.0
  </div>
  <span class="post-meta-divider">|</span>
  <div class="theme-info">Theme – <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> v7.7.1
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/blog/lib/anime.min.js"></script>
  <script src="/blog/lib/velocity/velocity.min.js"></script>
  <script src="/blog/lib/velocity/velocity.ui.min.js"></script>

<script src="/blog/js/utils.js"></script>

<script src="/blog/js/motion.js"></script>


<script src="/blog/js/schemes/pisces.js"></script>


<script src="/blog/js/next-boot.js"></script>




  















  

  

</body>
</html>
